package drds.global_transaction.common.util;

import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.util.Enumeration;
import java.util.regex.Pattern;

@Slf4j
public class Nets
{
	private static final String localhost = "127.0.0.1";
	private static final String anyhost = "0.0.0.0";
	private static final Pattern ip_pattern = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");
	private static volatile InetAddress local_address = null;

	public static String getHostAddress(SocketAddress socketAddress)
	{
		InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
		return inetSocketAddress.getAddress().getHostAddress();
	}

	//
	public static String getHostAddressAndPort(InetSocketAddress inetSocketAddress)
	{
		return inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort();
	}

	public static String getHostAddressAndPort(SocketAddress socketAddress)
	{
		return getHostAddressAndPort((InetSocketAddress) socketAddress);
	}

	public static InetAddress getLocalAddress()
	{
		if (local_address != null)
		{
			return local_address;
		}
		InetAddress localAddress = getLocalAddress0();
		local_address = localAddress;
		return localAddress;
	}

	private static InetAddress getLocalAddress0()
	{
		InetAddress inetAddress = null;
		try
		{
			inetAddress = InetAddress.getLocalHost();
			if (isValidAddress(inetAddress))
			{
				return inetAddress;
			}
		} catch (Throwable e)
		{
			log.warn("Failed to retrieving ip address, " + e.getMessage(), e);
		}
		try
		{
			Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
			if (enumeration != null)
			{
				while (enumeration.hasMoreElements())
				{
					try
					{
						NetworkInterface networkInterface = enumeration.nextElement();
						Enumeration<InetAddress> inetAddressEnumeration = networkInterface.getInetAddresses();
						if (inetAddressEnumeration != null)
						{
							while (inetAddressEnumeration.hasMoreElements())
							{
								try
								{
									InetAddress $inetAddress = inetAddressEnumeration.nextElement();
									if (isValidAddress($inetAddress))
									{
										return $inetAddress;
									}
								} catch (Throwable e)
								{
									log.warn("Failed to retrieving ip address, " + e.getMessage(), e);
								}
							}
						}
					} catch (Throwable e)
					{
						log.warn("Failed to retrieving ip address, " + e.getMessage(), e);
					}
				}
			}
		} catch (Throwable e)
		{
			log.warn("Failed to retrieving ip address, " + e.getMessage(), e);
		}
		log.error("Could not get local host ip address, will use 127.0.0.1 instead.");
		return inetAddress;
	}

	public static String getLocalIp()
	{
		InetAddress address = getLocalAddress();
		return address == null ? localhost : address.getHostAddress();
	}

	private static boolean isValidAddress(InetAddress inetAddress)
	{
		if (inetAddress == null || inetAddress.isLoopbackAddress())
		{
			return false;
		}
		String inetAddressHostAddress = inetAddress.getHostAddress();
		return (inetAddressHostAddress != null && //
				!anyhost.equals(inetAddressHostAddress) && //
				!localhost.equals(inetAddressHostAddress) && //
				ip_pattern.matcher(inetAddressHostAddress).matches());
	}

	//
	public static InetSocketAddress toInetSocketAddress(String address)
	{
		int i = address.indexOf(':');
		String host;
		int port;
		if (i > -1)
		{
			host = address.substring(0, i);
			port = Integer.parseInt(address.substring(i + 1));
		} else
		{
			host = address;
			port = 0;
		}
		return new InetSocketAddress(host, port);
	}
}
